using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Windows.Forms;
using System.Xml.Serialization;
using FlickrNet;
using Schedulr.Helpers;

namespace Schedulr.Configuration
{
    /// <summary>
    /// Represents the configuration for Schedulr.
    /// </summary>
    [Serializable]
    [XmlRoot(Namespace = "http://schemas.jelle.druyts.net/Schedulr")]
    public class SchedulrConfiguration
    {
        #region Properties

        private BindingList<Picture> queuedPictures = new BindingList<Picture>();

        /// <summary>
        /// Gets or sets the queued pictures.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")]
        public BindingList<Picture> QueuedPictures
        {
            get { return this.queuedPictures; }
        }

        private BindingList<UploadedPicture> uploadedPictures = new BindingList<UploadedPicture>();

        /// <summary>
        /// Gets or sets the uploaded pictures.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")]
        public BindingList<UploadedPicture> UploadedPictures
        {
            get { return this.uploadedPictures; }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SchedulrConfiguration"/> class.
        /// </summary>
        public SchedulrConfiguration()
        {
        }

        #endregion

        #region Load & Save

        /// <summary>
        /// Loads the configuration from file.
        /// </summary>
        /// <param name="fileName">The name of the file that contains the configuration.</param>
        private static SchedulrConfiguration Load(string fileName)
        {
            try
            {
                Logger.Log("Loading configuration from file: " + fileName, TraceEventType.Verbose);
                XmlSerializer serializer = new XmlSerializer(typeof(SchedulrConfiguration));
                using (FileStream stream = File.OpenRead(fileName))
                {
                    SchedulrConfiguration configuration = (SchedulrConfiguration)serializer.Deserialize(stream);
                    configuration.Upgrade();
                    return configuration;
                }
            }
            catch (Exception exc)
            {
                Logger.Log("Error while loading configuration", exc);
                throw;
            }
        }

        /// <summary>
        /// Attempts to upgrade the configuration, if needed.
        /// </summary>
        private void Upgrade()
        {
            // Upgrade the photoset field.
            if (FlickrServer.CurrentUserInfo != null && FlickrServer.CurrentUserInfo.Sets != null)
            {
                foreach (Photoset photoset in FlickrServer.CurrentUserInfo.Sets)
                {
                    foreach (Picture picture in this.queuedPictures)
                    {
                        if (string.Equals(photoset.Title, picture.Photoset, StringComparison.OrdinalIgnoreCase))
                        {
                            Logger.Log(string.Format(CultureInfo.CurrentCulture, "Upgrading picture, changing photoset '{0}' to id {1}.", picture.Photoset, photoset.PhotosetId), TraceEventType.Information);
                            picture.SetIds = new string[] { photoset.PhotosetId };
                            picture.Photoset = null;
                        }
                    }
                    foreach (Picture picture in this.uploadedPictures)
                    {
                        if (string.Equals(photoset.Title, picture.Photoset, StringComparison.OrdinalIgnoreCase))
                        {
                            Logger.Log(string.Format(CultureInfo.CurrentCulture, "Upgrading picture, changing photoset '{0}' to id {1}.", picture.Photoset, photoset.PhotosetId), TraceEventType.Information);
                            picture.SetIds = new string[] { photoset.PhotosetId };
                            picture.Photoset = null;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Saves this instance.
        /// </summary>
        public void Save()
        {
            Save(ConfigurationFileName);
        }

        /// <summary>
        /// Saves the configuration to file.
        /// </summary>
        /// <param name="fileName">The name of the file to which to save the configuration.</param>
        private void Save(string fileName)
        {
            try
            {
                Logger.Log("Saving configuration to file: " + fileName, TraceEventType.Verbose);
                XmlSerializer serializer = new XmlSerializer(typeof(SchedulrConfiguration));
                using (FileStream stream = File.Create(fileName))
                {
                    serializer.Serialize(stream, this);
                }
            }
            catch (Exception exc)
            {
                Logger.Log("Error while saving configuration", exc);
                throw;
            }
        }

        #endregion

        #region Import & Export

        /// <summary>
        /// Imports the current configuration from a file.
        /// </summary>
        /// <param name="fileName">The name of the file to import from.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public void Import(string fileName)
        {
            SchedulrConfiguration newConfiguration = Load(fileName);
            instance = newConfiguration;
            OnConfigurationChanged(EventArgs.Empty);
        }

        /// <summary>
        /// Exports the current configuration to a file.
        /// </summary>
        /// <param name="fileName">The name of the file to export to.</param>
        public void Export(string fileName)
        {
            Save(fileName);
        }

        #endregion

        #region ConfigurationChanged Event

        /// <summary>
        /// Raised when the configuration has changed.
        /// </summary>
        internal static event EventHandler<EventArgs> ConfigurationChanged;

        /// <summary>
        /// Raises the <see cref="E:ConfigurationChanged"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private static void OnConfigurationChanged(EventArgs e)
        {
            if (ConfigurationChanged != null)
            {
                ConfigurationChanged(null, e);
            }
        }

        #endregion

        #region ConfigurationFileChanged Event

        /// <summary>
        /// Occurs when the configuration file has changed externally to the application.
        /// </summary>
        internal static event EventHandler<EventArgs> ConfigurationFileChanged;

        /// <summary>
        /// Raises the <see cref="E:ConfigurationFileChanged"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private static void OnConfigurationFileChanged(EventArgs e)
        {
            if (ConfigurationFileChanged != null)
            {
                ConfigurationFileChanged(null, e);
            }
        }

        #endregion

        #region Static Members

        private static SchedulrConfiguration instance;

        private static FileSystemWatcher configurationWatcher;

        /// <summary>
        /// Gets the singleton configuration instance.
        /// </summary>
        public static SchedulrConfiguration Instance
        {
            get
            {
                if (instance == null)
                {
                    string fileName = ConfigurationFileName;
                    if (File.Exists(fileName))
                    {
                        if (configurationWatcher == null)
                        {
                            configurationWatcher = new FileSystemWatcher(Path.GetDirectoryName(fileName), Path.GetFileName(fileName));
                            configurationWatcher.NotifyFilter = NotifyFilters.LastWrite;
                            configurationWatcher.Changed += new FileSystemEventHandler(delegate(object sender, FileSystemEventArgs e)
                            {
                                Logger.Log("The configuration file has changed, change type: " + e.ChangeType.ToString(), TraceEventType.Verbose);
                                OnConfigurationFileChanged(EventArgs.Empty);
                            });
                            configurationWatcher.EnableRaisingEvents = true;
                        }
                        instance = Load(fileName);
                    }
                    else
                    {
                        instance = new SchedulrConfiguration();
                    }
                }
                return instance;
            }
        }

        /// <summary>
        /// Invalidates the current instance, forcing a reload the next time it is requested.
        /// </summary>
        public static void Invalidate()
        {
            instance = null;
        }

        /// <summary>
        /// Gets the name of the configuration file.
        /// </summary>
        private static string ConfigurationFileName
        {
            get
            {
                return Path.Combine(Application.LocalUserAppDataPath, "SchedulrConfiguration.xml");
            }
        }

        #endregion
    }
}